
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <math.h>
#include "fl.h"

#define debug1

typedef enum {
  TOK_ORIGIN,    /* keywords */
  TOK_IS,
  TOK_ROT,
  TOK_SCALE,
  TOK_FOR,
  TOK_T,
  TOK_TO,
  TOK_FROM,
  TOK_STEP,
  TOK_DRAW,
  
  TOK_SEMICO,       /* symbol */
  TOK_LBRACKET,
  TOK_RBRACKET,
  TOK_COMMA,
  TOK_PLUS,
  TOK_MINUS,
  TOK_MUL,
  TOK_DIV,
  TOK_POWER,

  TOK_FUNC,
  TOK_NUMBER,

  TOK_EOS,          /* other */
  TOK_UNKNOWN
} TOK;

typedef struct Token {
  TOK type;
  union {
    double value;
    double (*func)(double);
  };
} Token;

typedef struct Lexer {
  Token token;
  Token next;
  char* base;
  char* p;
  int currentline;
} Lexer;

const char* const token_string[] = {
  "origin",
  "is",
  "rot",
  "scale",
  "for",
  "t",
  "to",
  "from",
  "step",
  "draw",
  ";",
  "(",
  ")",
  ",",
  "+",
  "-",
  "*",
  "/",
  "**",
  "func",
  "number",
  "eos",
  "unknown"
};

#define next(lex) (lex->p++)
#define inclinenumber(lex) (lex->currentline++)
#define check_char(lex, ch) ((*lex->p) == ch)

static void scan_comment(Lexer* lex) {
  inclinenumber(lex);
  while(!check_char(lex, '\n')) 
    next(lex);
  next(lex);
  if(check_char(lex, '\r')) 
    next(lex);
}

static void keyword_or_function(Token* tok, char* begin, char* end) {
  int len = end - begin;
  switch(len){
  case 1: 
    if(*begin == 't') {
      tok->type = TOK_T;
    }
    else
          tok->type = TOK_UNKNOWN;
    break;
  case 2:
    if(strncmp(begin, "to", len) == 0) {
      tok->type = TOK_TO;
    }
    else if(strncmp(begin, "is", len) == 0) {
      tok->type = TOK_IS;
    }
    else if(strncmp(begin, "pi", len) == 0) {
      tok->type = TOK_NUMBER;
      tok->value = 3.1416;
    }
    else if(strncmp(begin, "ln", len) == 0) {
      tok->type = TOK_FUNC;
      tok->func = log;
    }
    else
          tok->type = TOK_UNKNOWN;
    break;
  case 3:
    if(strncmp(begin, "for", len) == 0) {
      tok->type = TOK_FOR;
    }
    else if(strncmp(begin, "rot", len) == 0) {
      tok->type = TOK_ROT;
    }
    else if(strncmp(begin, "sin", len) == 0) {
      tok->type = TOK_FUNC;
      tok->func = sin;
    }
    else if(strncmp(begin, "cos", len) == 0) {
      tok->type = TOK_FUNC;
      tok->func = cos;
    }
    else if(strncmp(begin, "tan", len) == 0) {
      tok->type = TOK_FUNC;
      tok->func = tan;
    }
    else if(strncmp(begin, "exp", len) == 0) {
      tok->type = TOK_FUNC;
      tok->func = exp;
    }
    else
          tok->type = TOK_UNKNOWN;
    break;
  case 4:
    if(strncmp(begin, "from", len) == 0) {
      tok->type = TOK_FROM;
    }
    else if(strncmp(begin, "step", len) == 0) {
      tok->type = TOK_STEP;
    }
    else if(strncmp(begin, "draw", len) == 0) {
      tok->type = TOK_DRAW;
    }
    else if(strncmp(begin, "sqrt", len) == 0) {
      tok->type = TOK_FUNC;
      tok->func = sqrt;
    }
    else
          tok->type = TOK_UNKNOWN;
    break;
  case 5:
    if(strncmp(begin, "scale", len) == 0) {
      tok->type = TOK_SCALE;
    }
    else
          tok->type = TOK_UNKNOWN;
    break;
  case 6:
    if(strncmp(begin, "origin", len) == 0) {
      tok->type = TOK_ORIGIN;
    }
    else
          tok->type = TOK_UNKNOWN;
    break;
  default:
    tok->type = TOK_UNKNOWN;
  }
  
}

static void scan_identifier(Lexer* lex, Token* tok) {
  char* begin = lex->p;
  char* end = NULL;
  while(isalnum(*lex->p) || check_char(lex, '_')) next(lex);
  end = lex->p;
  keyword_or_function(tok, begin, end);
}

static void scan_number(Lexer* lex, Token* tok) {
  char* begin;
  char* end;
  char buff[20];

  begin = lex->p;
  while(isdigit(*lex->p)) next(lex);
  if(check_char(lex, '.')) {
    next(lex);
    while(isdigit(*lex->p)) next(lex);
  }
  end = lex->p;
  strncpy(buff, begin, end - begin);
  buff[end - begin] = 0;
  tok->value = atof(buff);
  tok->type = TOK_NUMBER;
}

static int is_identifier_start(char c) {
  return (isalpha(c) || c == '_');
}

static int is_number_start(char c) {
  return isdigit(c);
}

static void scan(Lexer* lex, Token* tok) {
  for(;;){
    switch(*lex->p){
    case '\n':
      inclinenumber(lex);
      next(lex);
      continue;
    case '\r':
      next(lex);
      continue;
    case 0:
      tok->type = TOK_EOS;
      return;
    case '+':
      tok->type = TOK_PLUS;
      next(lex);
      return;
    case '-':
      next(lex);
      if(check_char(lex,'-')) {
	next(lex);
	scan_comment(lex);
	continue;
      }
      else{
	tok->type = TOK_MINUS;
	return;
      }
    case '*':
      next(lex);
      if(check_char(lex, '*')) {
	next(lex);
	tok->type = TOK_POWER;
	return;
      }
      tok->type = TOK_MUL;
      return;
    case '/':
      next(lex);
      if(check_char(lex, '/')) {
	next(lex);
	scan_comment(lex);
	continue;
      }
      else {
	tok->type = TOK_DIV;
	return;
      }
    case '(':
      tok->type = TOK_LBRACKET;
      next(lex);
      return;
    case ')':
      tok->type = TOK_RBRACKET;
      next(lex);
      return;
    case ',':
      tok->type = TOK_COMMA;
      next(lex);
      return;
    case ';':
      tok->type = TOK_SEMICO;
      next(lex);
      return;
    default:
      if(isspace(*lex->p)){
	next(lex);
	continue;
      }
      if(is_identifier_start(*lex->p))
	scan_identifier(lex, tok);
      else if(is_number_start(*lex->p))
	scan_number(lex, tok);
      else{
	next(lex);
	tok->type = TOK_UNKNOWN;
	continue;
      }
      return;
    } 
  }
}

static void next_token(Lexer* lex) {
  if(lex->next.type != TOK_EOS) {
    lex->token = lex->next;
    lex->next.type = TOK_EOS;
  }
  else {
    scan(lex, &lex->token);
  }
}

#ifdef debug

static void peek(Lexer* lex) {
  if(lex->next.type == TOK_EOS) {
    scan(lex, &lex->next);
  }
}

#endif

void init_lexer(Lexer* lexer, char* filename) {
  char* buffer = NULL;
  FILE* file = NULL;
  long filesize, copysize;
  file = fopen(filename, "rb");

  if(file == NULL){
    fprintf(stderr, "Can`t open file %s", filename);
    exit(1);
  }
  fseek(file, 0, 2);
  filesize = ftell(file);
  rewind(file);

  buffer = (char*)malloc(filesize + 1);
  if(buffer == NULL){
    fprintf(stderr, "No more memory");
    exit(2);
  }
  copysize = fread(buffer, 1, filesize, file);
  buffer[filesize] = 0;
  if(copysize != filesize){
    fprintf(stderr, "Not read the whole file");
    exit(3);
  }

  lexer->p = lexer->base = buffer;
  lexer->currentline = 1;
  lexer->token.type = TOK_EOS;
  lexer->next.type = TOK_EOS;
}

/* 
Parser :
   Program         ::= Statement SEMICO
   Statement       ::= OriginStatement | 
                       ScaleStatement  |
		       RotStatement    |
		       ForStatement
   OriginStatement ::= origin is ( Expression , Expression )
   ScaleStatement  ::= scale is ( Expression , Expression )
   RotStatement    ::= rot is Expression
   ForStatement    ::= for t from Expression to Expression step Expression
                       draw ( Expression , Expression )
   Expression      ::= Term [+|- Term]
   Term            ::= Factor [*|- Factor]
   Factor          ::= + Factor | - Factor | Component
   Component       ::= Atom POWER Component | Atom
   Atom            ::= number              |
                       t                   |
                       func ( Expression ) |
		       ( Expression )
*/


#define tok_type(lex) (lex->token.type)

static void check_next(Lexer* lex, TOK tok) {
  if(tok_type(lex) == tok) {
    next_token(lex);
  }
  else {
    printf("line %d: expect <%s>\n",lex->currentline, token_string[tok]);
    exit(1);
  }
}

static double expression(Lexer* lex, fl_t* fl);


/* Atom            ::= number              |
                       t                   |
                       func ( Expression ) |
		       ( Expression ) 
*/
static double atom(Lexer* lex, fl_t* fl) {
  if(tok_type(lex) == TOK_NUMBER) {
    double res = lex->token.value;
    next_token(lex);
    return res;
  }
  else if(tok_type(lex) == TOK_T) {
    next_token(lex);
    return fl->t;
  }
  else if(tok_type(lex) == TOK_FUNC) {
    double (*func)(double) = lex->token.func;
    double v = 0;
    next_token(lex);
    check_next(lex, TOK_LBRACKET);
    v = expression(lex, fl);
    check_next(lex, TOK_RBRACKET);
    return func(v);
  }
  else if(tok_type(lex) == TOK_LBRACKET) {
    double res;
    next_token(lex);
    res = expression(lex, fl);
    check_next(lex, TOK_RBRACKET);
    return res;
  }
  return 0;
}

/* Component       ::= Atom POWER Component | Atom */
static double component(Lexer* lex, fl_t* fl) {
  return atom(lex, fl);
}

/* Factor          ::= + Factor | - Factor | Component */
static double factor(Lexer* lex, fl_t* fl) {
  if(tok_type(lex) == TOK_PLUS) {
    next_token(lex);
    return factor(lex, fl);
  }
  else if(tok_type(lex) == TOK_MINUS) {
    next_token(lex);
    return 0 - factor(lex, fl);
  }
  return component(lex, fl);
}

/* Term            ::= Factor [*|- Factor] */
static double term(Lexer* lex, fl_t* fl) {
  double left = 0;
  double right = 0;
  left = factor(lex, fl);
  while(tok_type(lex) == TOK_MUL || tok_type(lex) == TOK_DIV){
    if(tok_type(lex) == TOK_MUL) {
      next_token(lex);
      right = factor(lex, fl);
      left *= right;
    }
    else if(tok_type(lex) == TOK_DIV) {
      next_token(lex);
      right = factor(lex, fl);
      left /= right;
    }
  }
  //  printf("term: %f\n", left);
  return left;
}

/* Expression      ::= Term [+|- Term] */
static double expression(Lexer* lex, fl_t* fl) {
  double left = 0;
  double right = 0;

  //  printf("expression\n");
  left = term(lex, fl);
  while(tok_type(lex) == TOK_PLUS || tok_type(lex) == TOK_MINUS) {
    if(tok_type(lex) == TOK_PLUS) {
      next_token(lex);
      right = term(lex, fl);
      left += right;
    }
    else if(tok_type(lex) == TOK_MINUS) {
      next_token(lex);
      right = term(lex, fl);
      left -= right;
    }
  }
  // printf("expr: %f\n", left);
  return left;
}

/* OriginStatement ::= origin is ( Expression , Expression ) */
static void origin_statement(Lexer* lex, fl_t* fl) {
  // printf("origin_statement\n");
  next_token(lex);
  check_next(lex, TOK_IS);
  check_next(lex, TOK_LBRACKET);
  fl->org_x = expression(lex, fl);
  check_next(lex, TOK_COMMA);
  fl->org_y = expression(lex, fl);
  check_next(lex, TOK_RBRACKET);
}

/* ScaleStatement  ::= scale is ( Expression , Expression ) */
static void scale_statement(Lexer* lex, fl_t* fl) {
  next_token(lex);
  check_next(lex, TOK_IS);
  check_next(lex, TOK_LBRACKET);
  fl->scal_x = expression(lex, fl);
  check_next(lex, TOK_COMMA);
  fl->scal_y = expression(lex, fl);
  check_next(lex, TOK_RBRACKET);
}

/* RotStatement    ::= rot is Expression */
static void rot_statement(Lexer* lex, fl_t* fl) {
  next_token(lex);
  check_next(lex, TOK_IS);
  fl->rot = expression(lex, fl);
}

#ifdef debug

static void draw(fl_t* fl) {
  printf("----------------------\n"
	 "org_x: %f\n"
	 "org_y: %f\n"
	 "rot:   %f\n"
	 "scal_x:%f\n"
	 "scal_y:%f\n"
	 "t:     %f\n"
	 "to:    %f\n"
	 "step:  %f\n"
	 "draw_x:%f\n"
	 "draw_y:%f\n"
	 "----------------------\n",
	 fl->org_x,
	 fl->org_y,
	 fl->rot,
	 fl->scal_x,
	 fl->scal_y,
	 fl->t,
	 fl->to,
	 fl->step,
	 fl->draw_x,
	 fl->draw_y
	 );
}

#else

static inline void draw(fl_t* fl) {
  double x,y,tx,ty;

  x = fl->draw_x;
  y = fl->draw_y;
  x *= fl->scal_x;
  y *= fl->scal_y;
  tx = x * cos(fl->rot) + y * sin(fl->rot);
  ty = y * cos(fl->rot) - x * sin(fl->rot);
  x = tx;
  y = ty;
  x += fl->org_x;
  y += fl->org_y;

  fl->draw_point(fl, x, y);
}

#endif

/* ForStatement    ::= for t from Expression to Expression step Expression
                       draw ( Expression , Expression )
 */
static void for_statement(Lexer* lex, fl_t* fl) {
  next_token(lex);
  check_next(lex, TOK_T);
  check_next(lex, TOK_FROM);
  fl->t = expression(lex, fl);
  check_next(lex, TOK_TO);
  fl->to = expression(lex, fl);
  check_next(lex, TOK_STEP);
  fl->step = expression(lex, fl);
  check_next(lex, TOK_DRAW);
  check_next(lex, TOK_LBRACKET);
  // save for for-loop
  *fl->backup = *lex;

  while(fl->t <= fl->to){
    *lex = *fl->backup;
    fl->draw_x = expression(lex, fl);
    check_next(lex, TOK_COMMA);
    fl->draw_y = expression(lex, fl);
    draw(fl);
    fl->t += fl->step;
  }
  check_next(lex, TOK_RBRACKET);
}

/* Statement       ::= OriginStatement | 
                       ScaleStatement  |
		       RotStatement    |
		       ForStatement
 */
static void statement(Lexer* lex, fl_t* fl) {
  // printf("statement\n");
  switch(tok_type(lex)) {
  case TOK_ORIGIN:
    origin_statement(lex, fl);
    break;
  case TOK_SCALE:
    scale_statement(lex, fl);
    break;
  case TOK_ROT:
    rot_statement(lex, fl);
    break;
  case TOK_FOR:
    for_statement(lex, fl);
    break;
  default:
    printf("stat line %d: syntax error\n", lex->currentline);
    exit(1);
  }
}

static void program(Lexer* lex, fl_t* fl) {
  next_token(lex);
  while(tok_type(lex) != TOK_EOS) {
    statement(lex, fl);
    check_next(lex, TOK_SEMICO);
  }
}

void fl_main(fl_t* fl, char* filename) {
  Lexer lex;
  init_lexer(&lex, filename);
  fl->backup = (Lexer*)malloc(sizeof(Lexer));
  program(&lex, fl);
  free(fl->backup);
}

#ifdef debug

void test_lexer(Lexer* lex) {
  next_token(lex);
  
  while(tok_type(lex) != TOK_EOS) {
    printf("line %d: %s\n",
	   lex->currentline, 
	   token_string[tok_type(lex)]);
    next_token(lex);
  }
}

int main(int argc, char** argv)
{
  fl_t fl;
  if(argc != 2){
    printf("Usage: fl filename\n");
    exit(1);
  }
  // test_lexer(&lex);
  fl_main(&fl, argv[1]);
  return 0;
}

#endif
